﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace JMacia.Gaming.d20.Core.Traits
{
    /// <summary>
    /// Represents an ability score
    /// </summary>
    public class Ability : Trait
    {
        #region Private members
        private int _damage;
        private int _drain;
        private bool _nonAbility;
        private AbilityModifier _modifier;
        #endregion

        #region Public static readonly field
        public static readonly ModifierType[] StandardAllowedBonusTypes = new ModifierType[]{
            SystemModifierType.Alchemical,
            SystemModifierType.Enhancement,
            SystemModifierType.Insight,
            SystemModifierType.Profane,
            SystemModifierType.Racial,
            SystemModifierType.Sacred,
            SystemModifierType.Untyped };
        #endregion

        #region Constructors
        ///// <summary>
        ///// Creates a new instance of an Ability score
        ///// </summary>
        //public Ability() : base(string.Empty, 10, new Modifiers(), StandardAllowedBonusTypes) { }

        /// <summary>
        /// Creates a new instance of an Ability score and initializes its name
        /// </summary>
        /// <param name="name">The name of the new ability</param>
        public Ability(AbilityType name) : this(name.ToString(), 10, new Modifiers(), 0, 0, StandardAllowedBonusTypes) { }

        /// <summary>
        /// Creates a new instance of an ability score and instantiates the name and unmodified value
        /// </summary>
        /// <param name="name">The name of the new ability</param>
        /// <param name="score">The initial unmodified value of the ability score</param>
        public Ability(AbilityType name, int score) : this(name.ToString(), score, new Modifiers(), 0, 0, StandardAllowedBonusTypes) { }

        /// <summary>
        /// Creates a new instance of an ability score and instantiates the name and unmodified value
        /// </summary>
        /// <param name="name">The name of the new ability</param>
        /// <param name="score">The initial unmodified value of the ability score</param>
        /// <param name="modifiers">A collection of modifiers to apply to the ability</param>
        public Ability(AbilityType name, int score, Modifiers modifiers) : this(name.ToString(), score, modifiers, 0, 0, StandardAllowedBonusTypes) { }

        /// <summary>
        /// Creates a new instance of an ability score and instantiates the name and unmodified value
        /// </summary>
        /// <param name="name">The name of the new ability</param>
        /// <param name="score">The initial unmodified value of the ability score</param>
        /// <param name="modifiers">A collection of modifiers to apply to the ability</param>
        /// <param name="damage">The current amount of ability damage this ability suffers from</param>
        /// <param name="drain">The current amount of ability drain this ability suffers from</param>
        public Ability(AbilityType name, int score, Modifiers modifiers, int damage, int drain) : this(name.ToString(), score, modifiers, damage, drain, StandardAllowedBonusTypes) { }

        /// <summary>
        /// Creates a new instance of an ability score and instantiates the name and unmodified value
        /// </summary>
        /// <param name="name">The name of the new ability</param>
        /// <param name="score">The initial unmodified value of the ability score</param>
        /// <param name="modifiers">A collection of modifiers to apply to the ability</param>
        /// <param name="damage">The current amount of ability damage this ability suffers from</param>
        /// <param name="drain">The current amount of ability drain this ability suffers from</param>
        /// <param name="allowedBonusTypes">The modifier types that this instance allows instead of the standard</param>
        public Ability(string name, int score, Modifiers modifiers, int damage, int drain, params ModifierType[] allowedBonusTypes)
            : base(name, score, modifiers, allowedBonusTypes)
        {
            _damage = damage;
            _drain = drain;
            _modifier = new AbilityModifier(this);
        }
        #endregion

        #region Public Properties

        /// <summary>
        /// The total value of the ability including all applicable modifiers, damage, and drain
        /// </summary>
        public override int Total
        {
            get
            {
                return base.Total - _damage - _drain;
            }
        }
        
        /// <summary>
        /// The ability modifier
        /// </summary>
        /// <seealso cref="NonAbility"/>
        public AbilityModifier Modifier
        {
            get
            {
                return _modifier;
            }
        }

        /// <summary>
        /// The amount of temporary ability damage done to this abilty score
        /// </summary>
        public int Damage
        {
            get { return _damage; }
            set { _damage = value; }
        }

        /// <summary>
        /// The amount of permanent ability drain done to this ability score
        /// </summary>
        public int Drain
        {
            get { return _drain; }
            set { _drain = value; }
        }

        /// <summary>
        /// Whether or not this ability is a nonability.
        /// </summary>
        /// <remarks>Ability modifiers (base and total) for a nonability are always 0</remarks>
        public bool NonAbility
        {
            get { return _nonAbility; }
            set { _nonAbility = value; }
        }

        /// <summary>
        /// The enumerated type of the ability, if defined
        /// </summary>
        public AbilityType? Type
        {
            get
            {
                if (Enum.IsDefined(typeof(AbilityType), Name))
                {
                    return Enum.Parse(typeof(AbilityType), Name, true) as AbilityType?;
                }
                return null;
            }
            set
            {
                if (value.HasValue)
                {
                    Name = value.ToString();
                }
            }
        }
        #endregion

        #region Public Methods
        public override string ToString()
        {
            int mod = Modifier.TotalValue;
            StringBuilder builder = new StringBuilder(base.ToString());
            builder.Append(" (");
            if (mod >= 0)
            {
                builder.Append('+');
            }
            builder.Append(mod);
            builder.Append(')');
            return builder.ToString();
        }
        public override bool Equals(object obj)
        {
            
            return base.Equals(obj);
        }
        public override int GetHashCode()
        {
            if(_nonAbility)
            {
                return Name.GetHashCode();
            }
            return base.GetHashCode() ^ _damage.GetHashCode() ^ _drain.GetHashCode();
        }
        #endregion

        #region Operators
        public static bool operator ==(Ability a, Ability b)
        {
            if (a._nonAbility && b._nonAbility && a.Name == b.Name)
            {
                return true;
            }
            return (a as Trait == b as Trait) &&
                !a._nonAbility &&
                !b._nonAbility &&
                a._damage == b._damage &&
                a._drain == b._drain;
        }
        public static bool operator !=(Ability a, Ability b)
        {
            return !(a == b);
        }
        #endregion

        #region Public Static Methods
        public static AbilityType? KeyAbility(SaveType save)
        {
            switch (save)
            {
                case SaveType.Fortitude:
                    return AbilityType.Constitution;
                case SaveType.Reflex:
                    return AbilityType.Dexterity;
                case SaveType.Will:
                    return AbilityType.Wisdom;
                default:
                    return null;
            }
        }
        #endregion
    }

    public class AbilityModifier : Modifier, ID20Modifier
    {
        private bool _autoSucceed = false;
        private bool _autofail = false;
        private Ability _parent;

        public AbilityModifier(Ability parent)
            : base(0, SystemModifierType.Ability)
        { }

        public override int BaseValue
        {
            get
            {
                if (_parent.NonAbility) { return 0; }
                return _parent.Base / 2 - 5;
            }
            set
            {
                throw new NotImplementedException("To modify the base value of an Ability Modifier, you must modify its parent ability");
            }
        }

        public override int TotalValue
        {
            get
            {
                if (_parent.NonAbility) { return 0; }
                return _parent.Total / 2 - 5;
            }
        }

        public int TotalValueForChecks
        {
            get
            {
                return TotalValue + MetaModifiers.TotalValue;
            }
        }

        #region ID20Modifier Members

        public bool AutoSucceed
        {
            get
            {
                return _autoSucceed;
            }
            set
            {
                _autoSucceed = value;
            }
        }

        public bool AutoFail
        {
            get
            {
                return _autofail;
            }
            set
            {
                _autofail = value;
            }
        }

        #endregion
    }

    /// <summary>
    /// Enumerates the six standard ability scores
    /// </summary>
    public enum AbilityType
    {
        Strength,
        Dexterity,
        Constitution,
        Intelligence,
        Wisdom,
        Charisma,
    }
}
